#include "Game.h"

using std::vector;

Game::Game ()
	:_count(0),
	_score(0),
	_level(100),
	_lives(100),
	_monster_kill_score(100),
	_high_score(0)
{
	LevelLoader level_data(_level);
	loadMap(level_data);
	loadObjects(level_data);
	plotMovablesToMap();
}

int Game::score()
{
	return _score;
}

int Game::digdugLives()
{
	return _lives;
}

int Game::highScore()
{
	return _high_score;
}
void Game::loadMap(LevelLoader &level_data)
{
	_map.set(level_data.getMapData());
}

int Game::level()
{
	return _level;
}

GameResult Game::update(Action instruction)
{
	if(_count%10 ==0)
	{
		clock_t begin=clock();
		routeMonsters(_map,  _digdug.location(), _monsters);
		clock_t end=clock();
		cout << "Time elapsed: " << double(diffclock(end,begin)) << " ms"<< endl;
	}

	PlayResult result = manageDigdugActions(instruction);
	if(result != MONSTER_DIES)
	{
		updateMonsters();
		result = moveObjects();
		if(result == DIGDUG_DIES)
		{
			if(_lives != 1)
			{
				_lives--;
				_map.removeAllMovables();
				LevelLoader level_data(_level);
				loadObjects(level_data);
			}
			
			else
				return GAME_OVER;
		}
	}
	else
	{
		result = MONSTER_DIES;
	}
	
	if(result == MONSTER_DIES)
	{
        _score = _score + _monster_kill_score;

		if(_monsters.size() == 0)
		{
			if(_level != 3)
			{
              	_level++;
				LevelLoader level_data(_level);
				loadMap(level_data);
				loadObjects(level_data);
				plotMovablesToMap();
			}
			else
       				return WIN;
		}

	}
	
	_count++;
	return IN_PROGRESS;
}

void Game::loadObjects(LevelLoader &level_data)
{
	_digdug = Digdug(level_data.getDigdugPosition());

	_monsters.clear();

	vector<Point> monster_locations =  level_data.getMonstersPosition();
	for(int i = 0; i <monster_locations.size(); i++)
	{
		_monsters.push_back(Monster(monster_locations.at(i)));
	}

	_projectile = Projectile(level_data.getDigdugPosition());
}

PlayResult Game::manageDigdugActions(Action instruction)
{
	//!if the instruction to shoot is given for the first time, check whether shooting is valid and place the projectile in the correct position if it is.
	if(instruction == SHOOT && _projectile.isShooting() == false)
	{
		DetectionResult result = _projectile.initializeShot(_digdug.direction(), _digdug.location(), _digdug.radius(), _map);
		if(result == CAN_MOVE) 
		{
			_map.plotMovable(_projectile.type(), _projectile.location(), _projectile.radius());
		}
		else if(result == MONSTER_DEAD)
		{
			destroyClosestMonster();
			return MONSTER_DIES;
		}
	}

	//! Shoot button must be held down in order to keep shooting
	else if(instruction != SHOOT && _projectile.isShooting() == true)
	{
		_projectile.shoot(false);
		_map.removeMovable(_projectile.location(), _projectile.radius());

		/*!The order of plotting ensures that projectile is plotted, then digdug then monsters.
		If the projectile shres the same location as digdug, it will be overwritten on the map when digdug is replotted on top of it*/
		_projectile.location(_digdug.location());
	}

	if(instruction != SHOOT && instruction != MOVE_NONE)
	{
		Direction dir = static_cast<Direction>(instruction);
		_digdug.setMoveDirection(dir);
	}
	return NO_CHANGE;
}

void Game::updateMonsters()
{
	for (int i = 0; i != _monsters.size(); i++)
	{
		_monsters.at(i).updateMoveDirection(_digdug.location(), _map);
		bool initial_ghost_status = _monsters.at(i).isGhost();
		if((_count+i* +5)%30== 0)
		_monsters.at(i).updateGhostMode(_digdug.location());
		if (initial_ghost_status !=  _monsters.at(i).isGhost())	//check if monster has turned into ghost
			_map.removeMovable(_monsters.at(i).location(), _monsters.at(i).radius());
	}
}


PlayResult Game::moveObjects()
{
	//! Move the projectile first
	if(_projectile.isShooting())
	{
		_map.removeMovable(_projectile.location(),_projectile.radius());

		DetectionResult projectile_result = _projectile.move(_map);
		if(projectile_result == CAN_MOVE)
		{
			_map.plotMovable(_projectile.type(), _projectile.location(), _projectile.radius());
		}
		else if(projectile_result == MONSTER_DEAD)
		{
			destroyClosestMonster();
			//!remove projectile from map
			_map.removeMovable(_projectile.location(), _projectile.radius());
			//!place projectile in the same position as digdug
			_projectile.location(_digdug.location());
			cout<<"MONSTER KILLED";
			return MONSTER_DIES;
		}
	}

	//!Move digdug
	_map.removeMovable(_digdug.location(),_digdug.radius());
	DetectionResult digdug_result = _digdug.move(_map);
	if(digdug_result == DIGDUG_DEAD)
	{return DIGDUG_DIES;}
	_map.plotMovable(_digdug.type(), _digdug.location(),_digdug.radius());
	if(_projectile.isShooting() == false)	//!Update projectiles position to match digdug's if the projectile is not being fired
	{_projectile.location(_digdug.location());}

	//!Move Monsters
	for(int i = 0; i != _monsters.size(); i++)
	{
//		if((_count +1)%2 ==0)
//		{
			if(_monsters.at(i).isGhost() == false)	//!Only remove the monster to the map if it is not in ghost mode.
			{
				_map.removeMovable(_monsters.at(i).location(), _monsters.at(i).radius());
			}
			DetectionResult monster_result = _monsters.at(i).move(_map);

			if(monster_result == DIGDUG_DEAD)
			{
				return DIGDUG_DIES;
			}
			else if(monster_result == MONSTER_DEAD)
			{
				//!create an iterator pointing to the start of the _monster vector
				vector<Monster>::iterator it = _monsters.begin();
				//!delete specific monster from game's data
				_monsters.erase(it + i);

				//!remove projectile from map
				_map.removeMovable(_projectile.location(), _projectile.radius());
				//!place projectile in the same position as digdug
				_projectile.location(_digdug.location());

				//!return that a monster has dies from the move function
				return MONSTER_DIES;
			}
			if(_monsters.at(i).isGhost() == false)	//!Only replot the monster to the map if it is not in ghost mode.
			{
				_map.plotMovable(_monsters.at(i).type(), _monsters.at(i).location(), _monsters.at(i).radius());
			}
		}

//	}
	return NO_CHANGE;
}

Map Game::map()
{
	return _map;
}

vector<Movables*> Game::movable_ptrs()
{
	//!No memory is being allocated, therefore smart pointers are not being used!
	vector<Movables*> movables;

	//! First add projectile
	Movables *projectile_ptr = &_projectile;
	movables.push_back(projectile_ptr);

	//! Second add digdug
	Movables *digdug_ptr = &_digdug;
	movables.push_back(digdug_ptr);

	//!Last add all the monsters
	for (int i = 0; i != _monsters.size(); i++)
	{
		movables.push_back(&_monsters.at(i));
	}
	return movables;
}

void Game::plotMovablesToMap()
{
	vector<Movables*> ptrs = movable_ptrs();
	for (int i = 0; i != ptrs.size(); i++)
	{
		_map.plotMovable((ptrs.at(i))->type(), (ptrs.at(i))->location(), (ptrs.at(i))->radius());
	}
}

void Game::destroyClosestMonster()
{
		//find closest monster and kill him
	int delta_x = 0;
	int delta_y = 0;

	int current_total_distance = 0;
	int prev_total_distance = 100;
	int monster_number = 0;


    for(int i = 0; i!= _monsters.size(); i++)
	{
		delta_x = _monsters.at(i).location().x() - _projectile.location().x();
		delta_y = _monsters.at(i).location().y() - _projectile.location().y();
		current_total_distance = abs(delta_x) - abs(delta_y);

		if(abs(current_total_distance) < abs(prev_total_distance))
		{
			prev_total_distance = current_total_distance;
			monster_number = i;
		}				

	}

	//remove dead monster from map and delete him
  	_map.removeMovable(_monsters.at(monster_number).location(), _monsters.at(monster_number).radius());
	vector<Monster>::iterator start_of_vec = _monsters.begin();
	_monsters.erase(start_of_vec + monster_number);
	//remove the projectile from the map and delete it
	_map.removeMovable(_projectile.location(), _projectile.radius());
	_projectile.shoot(false);
}

